package git.soulbgm.socket;

import git.soulbgm.utils.LogUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringEncoder;

import static git.soulbgm.utils.ChannelUtil.TCP_DELIMITER_BYTE;


/**
 * TCP服务端
 *
 * @author SoulBGM
 * @date 2021-03-29
 */
public class TcpServer {

    private final LogUtil log = new LogUtil(getClass());

    private ChannelInboundHandlerAdapter handler;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ServerBootstrap server;

    /**
     * TCP管理线程的线程数
     */
    private int tcpBossThreadNumber;

    /**
     * TCP传输数据的线程数
     */
    private int tcpWorkerThreadNumber;

    private static TcpServer tcpServer;

    private TcpServer() {
    }

    public static TcpServer getTcpServer() {
        if (tcpServer == null) {
            tcpServer = new TcpServer();
        }
        return tcpServer;
    }

    public void start(ChannelInboundHandlerAdapter handler) {
        start(handler, 1, 2);
    }

    public void start(ChannelInboundHandlerAdapter handler, int tcpBossThreadNumber, int tcpWorkerThreadNumber) {
        this.tcpBossThreadNumber = tcpBossThreadNumber;
        this.tcpWorkerThreadNumber = tcpWorkerThreadNumber;
        this.handler = handler;
    }

    private void initConfig() {
        server = new ServerBootstrap();
        // 1、绑定两个线程组分别处理客户端通道的accept和读写事件
        bossGroup = new NioEventLoopGroup(tcpBossThreadNumber);
        workerGroup = new NioEventLoopGroup(tcpWorkerThreadNumber);
        server.group(bossGroup, workerGroup);
        // 2、绑定服务器通道NioServerSocketChannel
        server.channel(NioServerSocketChannel.class)
                // 保持连接数
                .option(ChannelOption.SO_BACKLOG, 30000)
                // 有数据立即发送
                // .option(ChannelOption.TCP_NODELAY, true)
                // 保持连接
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                // 处理新连接
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    // 初始化通道
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 3、给读写事件的线程通道绑定handler去真正处理读写
                        ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer(TCP_DELIMITER_BYTE)));
                        ch.pipeline().addLast(handler);
                        ch.pipeline().addLast(new StringEncoder());
                    }
                });
    }

    public ChannelFuture bind(String host, int port) throws InterruptedException {
        return server.bind(host, port).sync();
    }

    public void shutdown() {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }

}
